﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using l1cs.server.model.Instance;
using l1cs.server.model;
using l1cs.server.serverpackets;
using l1cs.server.model.skill;

namespace l1cs.server.clientpackets
{
    public class C_Attack : ClientBasePacket
    {
        private static Logger _log = Logger.getLogger( typeof( C_Attack ).FullName );

        private int _targetX = 0;

        private int _targetY = 0;

        public C_Attack( byte[] decrypt, ClientThread client )
            : base( decrypt )
        {
            int targetId = readD();
            int x = readH();
            int y = readH();
            _targetX = x;
            _targetY = y;

            L1PcInstance pc = client.getActiveChar();

            if ( pc.isGhost() || pc.isDead() || pc.isTeleport() )
            {
                return;
            }

            L1Object target = L1World.getInstance().findObject( targetId );

            // 攻撃アクションをとれる状態か確認
            if ( pc.getInventory().getWeight240() >= 197 )
            { // 重量オーバー
                pc.sendPackets( new S_ServerMessage( 110 ) ); // \f1アイテムが重すぎて戦闘することができません。
                return;
            }

            if ( pc.isInvisble() )
            { // インビジビリティ、ブラインドハイディング中
                return;
            }

            if ( pc.isInvisDelay() )
            { // インビジビリティディレイ中
                return;
            }

            if ( target is L1Character )
            {
                if ( target.getMapId() != pc.getMapId()
                        || pc.getLocation().getLineDistance( target.getLocation() ) > 20D )
                { // ターゲットが異常な場所にいたら終了
                    return;
                }
            }

            if ( target is L1NpcInstance )
            {
                int hiddenStatus = ( (L1NpcInstance)target ).getHiddenStatus();
                if ( hiddenStatus == L1NpcInstance.HIDDEN_STATUS_SINK
                        || hiddenStatus == L1NpcInstance.HIDDEN_STATUS_FLY )
                { // 地中に潜っているか、飛んでいる
                    return;
                }
            }

            // 攻撃要求間隔をチェックする
            if ( Config.CHECK_ATTACK_INTERVAL )
            {
                int result;
                result = pc.getAcceleratorChecker()
                        .checkInterval( AcceleratorChecker.ACT_TYPE.ATTACK );
                if ( result == AcceleratorChecker.R_DISCONNECTED )
                {
                    return;
                }
            }

            // 攻撃アクションがとれる場合の処理
            if ( pc.hasSkillEffect( L1SkillId.ABSOLUTE_BARRIER ) )
            { // アブソルート バリアの解除
                pc.killSkillEffectTimer( L1SkillId.ABSOLUTE_BARRIER );
                pc.startHpRegeneration();
                pc.startMpRegeneration();
                pc.startMpRegenerationByDoll();
            }
            pc.killSkillEffectTimer( L1SkillId.MEDITATION );

            pc.delInvis(); // 透明状態の解除

            pc.setRegenState( L1PcInstance.REGENSTATE_ATTACK );

            if ( target != null && !( (L1Character)target ).isDead() )
            {
                target.onAction( pc );
            }
            else
            { // 空攻撃
                L1ItemInstance weapon = pc.getWeapon();
                int weaponId = 0;
                int weaponType = 0;
                L1ItemInstance arrow = null;
                L1ItemInstance sting = null;
                if ( weapon != null )
                {
                    weaponId = weapon.getItem().getItemId();
                    weaponType = weapon.getItem().getType1();
                    if ( weaponType == 20 )
                    {
                        arrow = pc.getInventory().getArrow();
                    }
                    if ( weaponType == 62 )
                    {
                        sting = pc.getInventory().getSting();
                    }
                }
                pc.setHeading( pc.targetDirection( x, y ) );
                if ( weaponType == 20 && ( weaponId == 190 || arrow != null ) )
                {
                    calcOrbit( pc.getX(), pc.getY(), pc.getHeading() ); // 軌道計算
                    if ( arrow != null )
                    { // 矢がある場合
                        pc.sendPackets( new S_UseArrowSkill( pc, 0, 66, _targetX,
                                _targetY, true ) );
                        pc.broadcastPacket( new S_UseArrowSkill( pc, 0, 66, _targetX,
                                _targetY, true ) );
                        pc.getInventory().removeItem( arrow, 1 );
                    }
                    else if ( weaponId == 190 )
                    { // サイハの弓
                        pc.sendPackets( new S_UseArrowSkill( pc, 0, 2349, _targetX,
                                _targetY, true ) );
                        pc.broadcastPacket( new S_UseArrowSkill( pc, 0, 2349,
                                _targetX, _targetY, true ) );
                    }
                }
                else if ( weaponType == 62 && sting != null )
                {
                    calcOrbit( pc.getX(), pc.getY(), pc.getHeading() ); // 軌道計算
                    pc.sendPackets( new S_UseArrowSkill( pc, 0, 2989, _targetX,
                            _targetY, true ) );
                    pc.broadcastPacket( new S_UseArrowSkill( pc, 0, 2989, _targetX,
                            _targetY, true ) );
                    pc.getInventory().removeItem( sting, 1 );
                }
                else
                {
                    pc.sendPackets( new S_AttackPacket( pc, 0, ActionCodes
                            .ACTION_Attack ) );
                    pc.broadcastPacket( new S_AttackPacket( pc, 0, ActionCodes
                            .ACTION_Attack ) );
                }
            }
        }

        private void calcOrbit( int cX, int cY, int head )
        {
            float disX = Math.Abs( cX - _targetX );
            float disY = Math.Abs( cY - _targetY );
            float dis = Math.Max( disX, disY );
            float avgX = 0;
            float avgY = 0;
            if ( dis == 0 )
            {
                if ( head == 1 )
                {
                    avgX = 1;
                    avgY = -1;
                }
                else if ( head == 2 )
                {
                    avgX = 1;
                    avgY = 0;
                }
                else if ( head == 3 )
                {
                    avgX = 1;
                    avgY = 1;
                }
                else if ( head == 4 )
                {
                    avgX = 0;
                    avgY = 1;
                }
                else if ( head == 5 )
                {
                    avgX = -1;
                    avgY = 1;
                }
                else if ( head == 6 )
                {
                    avgX = -1;
                    avgY = 0;
                }
                else if ( head == 7 )
                {
                    avgX = -1;
                    avgY = -1;
                }
                else if ( head == 0 )
                {
                    avgX = 0;
                    avgY = -1;
                }
            }
            else
            {
                avgX = disX / dis;
                avgY = disY / dis;
            }

            int addX = (int)Math.Floor( ( avgX * 15 ) + 0.59f );
            int addY = (int)Math.Floor( ( avgY * 15 ) + 0.59f );

            if ( cX > _targetX )
            {
                addX *= -1;
            }
            if ( cY > _targetY )
            {
                addY *= -1;
            }

            _targetX = _targetX + addX;
            _targetY = _targetY + addY;
        }
    }
}